function similarityEvaluation(s1, s2) {
    // Special principles
    if (s1 == s2) return '100%'
    if (!s1 || !s2) return '0%'

    // Whole principle
    let v = 0
    let principles = [
        [s => s.length, 10], // same length
        [s => s.length > 1 && s[0] + s.slice(-1), 20], // same first & last
        [s => (s.match(/[a-z]/ig) || []).length, 5], // same letters count
        [s => (s.match(/\d/g) || []).length, 5], // same digits count
        [s => (s.match(/\s+/g) || []).length, 5], // same words count
        [s => [...s].sort().join`` == s && new Set(s).size == s.length, 10], // same asc
        [s => [...s].sort().reverse().join`` == s && new Set(s).size == s.length, 10], // same desc
        [s => s.length == 1, -10], // compensate for both asc and desc -> length == 1
        [s => [...s].reverse().join`` == s && s.length > 1, 10], // both palindrome
        [s => [...s].reduce((a, b) => a + b.charCodeAt(), 0), 20], // equal ascii sum
        [s => s == s1 ? [...s].reverse().join``.toLowerCase() : s.toLowerCase(), 20], // rev equal
        [s => s == s1[0].repeat(s.length), 40] // repeated char
    ]
    principles.forEach(([f, n]) => {
        if (f(s1) && f(s1) == f(s2)) v += n
    })

    // Individual principle
    function counter(s) {
        let arr = Array(256).fill(0)
        for (let c of s) arr[c.charCodeAt()]++
        return arr
    }
    let letters = 0, digits = 0, others = 0
    let c1 = counter(s1), c2 = counter(s2)
    c1.forEach((v, i) => {
        if (String.fromCharCode(i).match(/[a-z]/i))
            letters += Math.min(c1[i], c2[i])
        else if (String.fromCharCode(i).match(/\d/))
            digits += Math.min(c1[i], c2[i])
        else
            others += Math.min(c1[i], c2[i])
    })

    v += 2 * Math.min(5, letters) + 2 * Math.min(5, digits) + 4 * Math.min(5, others)

    // Whole principle: 1-char diff
    for (let i = 0; i < Math.max(s1.length, s2.length); ++i) {
        if (s1.slice(0, i) + s1.slice(i + 1) == s2 ||
            s1.slice(0, i) + s2[i] + s1.slice(i + 1) == s2 ||
            s2.slice(0, i) + s2.slice(i + 1) == s1 ||
            s2.slice(0, i) + s1[i] + s2.slice(i + 1) == s1) {
            v += 30
            break
        }
    }

    // Final special principle
    return Math.min(99, v) + '%'
}